using System;
using System.Collections.Generic;
using System.Text;

namespace Woz
{
    /// <summary>
    /// defines propagate last change action that is used to undo and redo
    /// propagate last change
    /// </summary>
    internal class PropagateLastChangeAction : Woz.Action
    {
        /// <summary>
        /// creates a propagate last change action with the given changes as actions
        /// and screens
        /// </summary>
        /// <param name="pScreens">the screens to propagate the changes to</param>
        /// <param name="pActions">the changes to propagate</param>
        public PropagateLastChangeAction(Woz.Screen[] pScreens, Woz.Action[] pActions)
        {
            // set the screens to propagate changes to
            this.mScreens = pScreens;

            // set the changes to propagate to those screens
            this.mChanges = pActions;

            // initialize the saved actions
            this.mSavedActions = new Stack<Woz.Action>();

            // update the action string
            this.mActionString = String.Format("Propagated {0} Changes to {1} Screens",
                pActions.Length, pScreens.Length);
        }

        /// <summary>
        /// gets the name of the tool this action belongs to none in this case
        /// </summary>
        public override string ToolName
        {
            get
            {
                return "";
            }
        }

        /// <summary>
        /// gets whether this action can be propagated or not
        /// </summary>
        public override bool Propagatable
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// propagates the last changes
        /// </summary>
        /// <param name="pScreen">not used for non propagateable action</param>
        protected override void OnExecute(Woz.Screen pScreen)
        {
            int nchanges = this.mChanges.Length;
            int nscreens = this.mScreens.Length;
            float total = 0;
            float increment = (100.0f / (float)(nscreens * nchanges));

            // start the progress bar
            Woz.ResourceManager.Instance.ProgressStartMethod(
                String.Format("Propagating Last {0} Changes to {1} Screens",
                nchanges,
                nscreens),
                null);

            // go through all of the screens and apply all of the 
            // changes to each one
            for (int i = 0; i < this.mScreens.Length; i++)
            {
                for (int j = 0; j < this.mChanges.Length; j++)
                {
                    // apply the change
                    this.mChanges[j].ApplyTo(this.mScreens[i]);
                    Console.WriteLine("{0} to {1}", this.mChanges[j].ToString(), this.mScreens[i].Name);
                    // update the status
                    total += increment;
                    Woz.ResourceManager.Instance.ProgressUpdateMethod((int)total);
                }
            }

            // get the list of all the actions
            Woz.Action[] allActions = Woz.ActionManager.Instance.GetActions();

            // remove all actions from undo stack upto the last selected action
            foreach (Woz.Action action in allActions)
            {
                // save it
                this.mSavedActions.Push(Woz.ActionManager.Instance.PopActionFromCurrentUndoStack());

                // see if the action is the last selected action
                if (action == this.mChanges[nchanges - 1])
                {
                    break;
                }
            }

            // stop the progress bar
            Woz.ResourceManager.Instance.ProgressEndMethod(0);
        }

        /// <summary>
        /// unpropagates the last changes
        /// </summary>
        /// <param name="pScreen">not used for non propagateable action</param>
        protected override void OnUnExecute(Woz.Screen pScreen)
        {
            int nchanges = this.mChanges.Length;
            int nscreens = this.mScreens.Length;
            float total = 0;
            float increment = (100.0f / (float)(nscreens * nchanges));

            // start the progress bar
            Woz.ResourceManager.Instance.ProgressStartMethod(
                String.Format("Unpropagating {0} Changes from {1} Screens",
                nchanges,
                nscreens),
                null);

            // go through all of the screens and apply all of the 
            // changes to each one
            for (int i = this.mScreens.Length - 1; i >= 0; i--)
            {
                for (int j = this.mChanges.Length - 1; j >= 0; j--)
                {
                    // apply the change
                    this.mChanges[j].UnApplyTo(this.mScreens[i]);

                    // update the status
                    total += increment;
                    Woz.ResourceManager.Instance.ProgressUpdateMethod((int)total);
                }
            }

            // pop this propagate last change action
            Woz.Action self = Woz.ActionManager.Instance.PopActionFromCurrentUndoStack();

            // add all saved actions to undo stack
            while (this.mSavedActions.Count > 0)
            {
                Woz.ActionManager.Instance.PushActionOnCurrentUndoStack(this.mSavedActions.Pop());
            }

            // push the propagate last change action
            Woz.ActionManager.Instance.PushActionOnCurrentUndoStack(self);

            // stop the progress bar
            Woz.ResourceManager.Instance.ProgressEndMethod(0);
        }

        /// <summary>
        /// the changes to propagate
        /// </summary>
        private Woz.Action[] mChanges;

        /// <summary>
        /// stack of saved actions
        /// </summary>
        private Stack<Woz.Action> mSavedActions;

        /// <summary>
        /// the screens to propagate the changes to
        /// </summary>
        private Woz.Screen[] mScreens;
    }
}
